/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1/commercial
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may use this file in compliance with the License.
 * Alternatively, if you can't copy with the License, namely if you do not
 * want to provide source code of your modifications to this file, you may
 * negotiate a commercial licence with the initial developer.
 * You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is COID.
 *
 * The Initial Developer of the Original Code is
 * PosAm.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Brano Kemen
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#ifndef __COID_SERVER_SERVER__HEADER_FILE__
#define __COID_SERVER_SERVER__HEADER_FILE__

#include "coid/comm/segarray.h"
#include "coid/comm/str.h"
//#include "coid/comm/tree/ttree.h"
//#include "coid/comm/interfnc.h"
#include "coid/comm/version.h"
#include "coid/comm/binstream/netstream.h"
#include "coid/comm/binstream/nullstream.h"

#include "serverface.h"
#include "pluginscanner.h"
#include "treemgr.h"
#include "service.h"
#include "connection.h"
#include "logcoid.h"


#include "accountmgr/client/accountmgr_client.h"


#include "coid/comm/pthreadx.h"

namespace coid {

////////////////////////////////////////////////////////////////////////////////
extern version  GserverVersion;

extern "C" {
    int coid_server_start( int& port, int (*cancel_cbk)(int) );
    int coid_server_stop();
    int coid_get_version( charstr & buf );
}


struct coid_cmd_interface;
struct ConnectionCoid;


#define SRVROOT             SINGLETON(ServerGlobal)
//#define SRVROOTA            SINGLETON_ALIVE(ServerGlobal)
#define SRVROOTA            SINGLETON(ServerGlobal)

#define GUARDDEADLIST       MXGUARD(_deadlist_mutex)
#define GUARDAUTODLIST      MXGUARD(_autodestroy_mutex)


////////////////////////////////////////////////////////////////////////////////
///Server root
struct RootCoid : CoidNode
{
    RootCoid()
    {
        _graveyards = 0;
        _mutex.set_name( "RootCoid" );
    }

    virtual token short_name() const        { static token _T("root"); return _T; }

    virtual opcd dispatch_cmd( const token& cmd, command_tokens& arg, txtstream& reply );
    virtual opcd list_cmds( const token& cmd, dynarray<charstr>& cmds ) const;


    static void* thread_logdump(void* p)    { return ((RootCoid*)p)->logdump(); }
    void spawn_logdump()
    {
        spawn_thread( (t_thread_fnc)thread_logdump, this, true, "thread_logdump" );
    }

    static void* thread_gravehandle(void* p)    { return ((RootCoid*)p)->gravehandle(); }
    void spawn_graveyard_loop()
    {
        spawn_thread( (t_thread_fnc)thread_gravehandle, this, true, "graveyard loop" );
    }

    void* logdump();
    void* gravehandle();

    IMPLEMENTS_VIRTUAL_INTERFACE_ttree_ifc_BEGIN_PURE(RootCoid,"0.0",CoidNode);
    IMPLEMENTS_VIRTUAL_INTERFACE_ttree_ifc_END;

protected:
    int _graveyards;
};


#define COID_LOCK(id,fn)       ServerGlobal::GUARD __lock(*this, id, fn)
#define GCOID_LOCK(id,fn)      ServerGlobal::GUARD __lock(SRVROOTA, id, fn)



////////////////////////////////////////////////////////////////////////////////
class ServerGlobal
{
    typedef void (* t_sig_handler) (int);

    static t_sig_handler    __sig_handler;

    static void coid_sig_handler( int sig );



public:

    static const char* class_name()   { return "ServerGlobal"; }

    ServerGlobal();
    ~ServerGlobal()
    {
        _run_updater = 0;
    }


public:

    opcd init( int (*cancel_cbk)(int), int& coidport );
    opcd stop();
    void cancel( int restart )
    {
        if(_cancel_cbk)
            _cancel_cbk(restart);

        stop();
        exit(0);
    }

    bool being_destroyed() const { return _being_destroyed; }

    binstream& post_server_msg( uchar msgtype, uchar verb )
    {
        return *_log.get_entry( 0, msgtype, verb, 0, 0 );
    }
    
    void prune_dead();
    void add_to_dead_list( LNID node )
    {
        //GUARDDEADLIST;
        //*_deadlist.add(1) = node;
        _objmgr.request_detach( node, false );
    }
/*
    opcd detach_children( LNID node )
    {
        dynarray<TreeMgr::ObjList> dl;

        return _objmgr.object_detach( node, dl, true );
    }
*/

    int run_update_plglist()
    {
        if( _run_updater > 1 )
            return _run_updater--;
        return _run_updater;
    }

    int ask_run_update_plglist()
    {
        return ++_run_updater;
    }
    

    ///Server log object
    SVCLOG  _log;

    ///Object manager guarded singleton
    TreeMgr _objmgr;

    ///Thread manager guarded singleton
    ThreadMgr _thrmgr;

    ///Account manager service client
    AccountMgr_client _accmgr;

    uint _accpcoid_nodeid;

    template<class T>
    opcd attach( T* node, NODEREF<T>& res, LNID parid, ushort verb, LNID hookto = UMAX32 )
    {
        opcd e = _objmgr.object_attach( node, res, parid, hookto );
        if(e)
            return e;

        e = res->initialize();

        //set object name if not already
        ConnectionCoid* conn = ConnectionCoid::get_active_conn();

        charstr host;
        if( res->object_name().is_empty()  &&  conn )
            res->object_name( conn->_client_addr.getHostName( host, true ) );

        binstream& log = res->post_attach( (uchar)verb );
        log << "attaching [" << T::GET_INTERFACE().get_class_name() << "]\t(" << res->object_name() << ")" << EOM;

        return 0;
    }
    
    opcd detach( uint oid, uchar verb, bool children_only=false );


    ushort get_coidaccp_port() const            { return _coidaccport; }
    const charstr& get_coidaccpstring() const   { return _coidaddrstring; }
    const netAddress& get_coidaccp() const      { return _coidaddr; }


    opcd parse_path( LNID idc, LNID idh, const token& path, dynarray<uint>& out, bool firstturn = true ) const;


    opcd start_acceptor( NODEREF<ServiceCoid>& svc, int port );
    opcd try_start_coid_acceptor( int& port );
    opcd start_coid_acceptor( int& port );
    opcd start_tower_acceptor( int port );


    opcd register_autodestroy( uint id )
    {
        GUARDAUTODLIST;

        uints pos = _autodestroy.lower_bound(id);
        if( pos >= _autodestroy.size() || _autodestroy[pos] != id )
            *_autodestroy.ins(pos) = id;
        return 0;
    }

    opcd unregister_autodestroy( uint id )
    {
        GUARDAUTODLIST;

        uints pos = _autodestroy.lower_bound(id);
        if( pos < _autodestroy.size() && _autodestroy[pos] == id )
            _autodestroy.del(pos);
        return 0;
    }

    opcd check_autodestroy()
    {
        time_t tnow;
        time(&tnow);

        GUARDAUTODLIST;

        for( uints i=0; i<_autodestroy.size(); ++i )
        {
            if( _objmgr.check_node_autodestroy( _autodestroy[i], tnow ) )
                add_to_dead_list( _autodestroy[i] );
        }

        return 0;
    }
    
private:

/*
    opcd get_hooked( LNID key, dynarray<LNID>& lst ) const
    {
        REF id;
        COID_LOCK(key,"get_hooked");
        if( _tree.get_node(key, id) )
        {
            _tree.get_hooked (id, (dynarray<COID>&)lst);
            return 0;
        }
        return ersOUT_OF_RANGE "index";
    }
*/


    //opcd gen_digraph (LNID root, txtstream& out) const;

    
    ////////////////////////////////////////////////////////////////////////////////
    friend struct AcceptorCoid;
    friend struct ConnectionCoid;



    mutable comm_mutex  _mutex;

private:

    int (*_cancel_cbk)(int);

    cmd_interface* new_cmdin( CoidNode* pci, cmd_interface& cmdin );

    ushort          _coidaccport;
    charstr         _coidaddrstring;
    netAddress      _coidaddr;

    //tree
    //comm_mutex      _tree_mutex;
    typedef ttree<CoidNode,ttree_ifc>       t_tree;


    comm_mutex      _autodestroy_mutex;
    dynarray<uint>  _autodestroy;

    //deadlist
    //comm_mutex      _deadlist_mutex;
    //dynarray<LNID>  _deadlist;
/*
    uint pop_deadlist_obj()
    {
        GUARDDEADLIST;

        uint n = _deadlist.size();
        for( uint i=0; i<n; ++i )
        {
            if( _deadlist[i] )
            {
                uint cn = _deadlist[i];
                _deadlist[i] = 0;
                return cn;
            }
        }

        _deadlist.reset();
        return 0;
    }
*/
    charstr         _cwd;
    int             _run_updater;

    bool            _being_destroyed;

 
    //opcd get_digraph_node (const REF& root, txtstream& dg, dynarray<uint>& con) const;
};


} // namespace coid

#endif //__COID_SERVER_SERVER__HEADER_FILE__
